package com.spring.business.configure.service.impl;

import com.spring.business.configure.dao.ConfigureMapper;
import com.spring.business.configure.dao.ConfigureSerialMapper;
import com.spring.business.configure.dao.FileInfoMapper;
import com.spring.business.configure.dao.SysConfMapper;
import com.spring.business.configure.entity.ConfigureSerial;
import com.spring.business.configure.entity.DictionaryDetail;
import com.spring.business.configure.entity.FileInfo;
import com.spring.business.configure.entity.SysConf;
import com.spring.business.configure.pojo.SyncQueue;
import com.spring.business.configure.pojo.dto.DictListDTO;
import com.spring.business.configure.pojo.dto.SysConfDTO;
import com.spring.business.configure.pojo.vo.DictTypeVO;
import com.spring.business.configure.pojo.vo.DictValueVO;
import com.spring.business.configure.pojo.vo.SysConfVO;
import com.spring.business.configure.service.IConfigureService;
import com.spring.business.configure.utils.CharsUtil;
import com.spring.business.configure.utils.DateTimeUtil;
import com.spring.module.cache.RedisLock;
import com.spring.module.file.FileItem;
import com.spring.module.file.IFileService;
import com.spring.starter.resolver.BeanResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * 配置管理业务层
 */
@Service
public class ConfigureServiceImpl implements IConfigureService
{
	private static final Logger logger = LoggerFactory.getLogger(ConfigureServiceImpl.class);

	private static final Map<String, SyncQueue<String>> bizTagMap = new HashMap<>();

	private static final Object LOCK = new Object();

	private static final Integer SERIAL_STEP = 100;

	private static final Integer SERIAL_LENGTH_FOUR = 4;
	private static final Integer SERIAL_LENGTH_SIX = 6;

	@Autowired
	private BeanResolver beanResolver;

	@Autowired
	private SysConfMapper sysConfMapper;

	@Autowired
	private FileInfoMapper fileInfoMapper;

	@Autowired
	private ConfigureMapper configureMapper;

	@Resource
	private ConfigureSerialMapper configureSerialMapper;

	@Autowired
	private RedisLock redisLock;

	@Autowired
	private IFileService fileService;

	/**
	 * 根据指定数据字典类型获取数据字典值
	 * @param code
	 * @return
	 */
	@Override
	public DictTypeVO getDitType(String code)
	{
		// 查询字典数据
		List<DictionaryDetail> ditTypeList = configureMapper.getDitTypeByCode(code);
		List<DictValueVO> dictValueVOS = beanResolver.mapAsList(ditTypeList, DictValueVO.class);
		return DictTypeVO.builder().code(code).dictValueVOList(dictValueVOS).build();
	}

	/**
	 * 根据字典类型列表获取多数据字典
	 * @param dictListDTO
	 * @return
	 */
	@Override
	public List<DictTypeVO> getDitTypeList(DictListDTO dictListDTO)
	{
		List<DictTypeVO> dictTypeVOList = new ArrayList<>();
		dictListDTO.getDictCodeList().forEach(e -> {
			// 查询字典数据
			List<DictionaryDetail> ditTypeList = configureMapper.getDitTypeByCode(e);
			List<DictValueVO> dictValueVOS = beanResolver.mapAsList(ditTypeList, DictValueVO.class);
			dictTypeVOList.add(DictTypeVO.builder().code(e).dictValueVOList(dictValueVOS).build());
		});
		return dictTypeVOList;
	}

	@Override
	public List<SysConf> getTenantSysConf(String tenant)
	{
		return sysConfMapper.getTenantSysConf(tenant);
	}

	/**
	 * 根据指定配置类型获取配置数据
	 * @param code
	 * @return
	 */
	@Override
	public SysConfVO getSysConf(String code)
	{
		// 查询字典数据
		SysConf sysConf = sysConfMapper.getSysConfByCode(code);
		return beanResolver.map(sysConf, SysConfVO.class);
	}

	@Override
	public List<SysConf> getSysConfLike(String code)
	{
		return sysConfMapper.getSysConfByCodeLike(code);

	}

	@Override
	public void updateTenantSysConf(SysConfDTO sysConfDTO)
	{
		sysConfMapper.updateTenantSysConf(sysConfDTO);
	}

	@Override
	public Integer getUserID()
	{
		return Integer.valueOf(getSerialCodeBybizTag("USERID"));
	}

	/**
	 * 序列号组成： 业务编码(1) + 年月日(8) + 时分秒(6) + 序列号(4) ,总计19位
	 * 随着日订单量增加，序列号会增长
	 * @param module
	 * @return
	 */
	@Override
	public String getSerialCode(String module)
	{
		String currentDate = DateTimeUtil.getDayTimeByFormat(System.currentTimeMillis(), DateTimeUtil.FORMAT_yyyyMMdd);
		String bizTag = module.concat(currentDate);
		String code = getSerialCodeBybizTag(bizTag);

		String time = DateTimeUtil.getDayTimeByFormat(System.currentTimeMillis(), DateTimeUtil.FORMAT_HHmmss);

		return currentDate.concat(time).concat(code);
	}

	public String getSerialCodeBybizTag(String bizTag)
	{
		return getSerialCodeBybizTag(bizTag, SERIAL_STEP);
	}


	/**
	 * 根据业务编码获取序列号，调用方通过当天日期支持每天从0开始计数，也可以传入固定bizTag从而持续计数
	 * 注意：这里仅仅是生成序列号，业务编码需要在序列号的基础上行进一步扩展
	 * @param bizTag
	 * @return
	 */
	public String getSerialCodeBybizTag(String bizTag, Integer serialStep)
	{

		String code = "";
		SyncQueue<String> bizTagCodes = null;

		Long currentTime = System.currentTimeMillis();
		synchronized (LOCK) //TODO 本地锁，分布式部署，注意更换为分布式锁
		//if (redisLock.lock(bizTag, UUID.randomUUID().toString().replace("-", "")))
		{
			ConfigureSerial serial = configureSerialMapper.selectOneByBizTagAndCreateTime(bizTag);
			if (null != serial)
			{
				// 发号
				SyncQueue<String> codes = bizTagMap.get(serial.getBizTag());
				if (null != codes && codes.size() > 0)
				{
					code = codes.poll();
				} else
				{
					bizTagMap.remove(serial.getBizTag());// 作废历史
					// 更新号段
					bizTagCodes = new SyncQueue<>();
					Long maxId = serial.getMaxId();
					final long newMaxId = (maxId + serial.getStep());
					while (maxId < newMaxId)
					{
						maxId = maxId + 1;
						bizTagCodes.offer(CharsUtil.fillPrefixZeroString(maxId.intValue(), SERIAL_LENGTH_FOUR));
					}
					bizTagMap.put(serial.getBizTag(), bizTagCodes);

					// 发号
					codes = bizTagMap.get(serial.getBizTag());
					if (null != codes && codes.size() > 0)
					{
						code = codes.poll();
					}

					// 更新配置
					serial.setMaxId(newMaxId);
					serial.setUpdateTime(currentTime);
					configureSerialMapper.updateByPrimaryKeySelective(serial);
				}
			} else
			{
				// 配置生成
				serial = new ConfigureSerial();
				serial.setBizTag(bizTag);
				serial.setMaxId(100L);
				serial.setStep(serialStep);
				serial.setCreateTime(currentTime);
				configureSerialMapper.insertSelective(serial);

				bizTagMap.remove(serial.getBizTag());// 作废历史

				// 更新号段
				bizTagCodes = new SyncQueue<>();
				long maxId = 1L;
				while (maxId <= serial.getStep())
				{
					bizTagCodes.offer(CharsUtil.fillPrefixZeroString((int)maxId, SERIAL_LENGTH_FOUR));
					//这里将 maxId 从1开始，与循环次数保持一致。解决序号多发的问题
					maxId = maxId + 1;
				}
				bizTagMap.put(serial.getBizTag(), bizTagCodes);
				// 发号
				SyncQueue<String> codes = bizTagMap.get(serial.getBizTag());
				if (null != codes && codes.size() > 0)
				{
					code = codes.poll();
				}
			}

			//redisLock.unlock(bizTag);
		}

		return code;
	}

	/**
	 * 获取文件信息
	 *
	 * @param fileCode
	 * @return
	 */
	@Override
	public FileInfo getFile(String fileCode) {
		return fileInfoMapper.getFileInfoByCode(fileCode);
	}

	/**
	 * 删除文件
	 *
	 * @param fileCode
	 * @return
	 */
	@Override
	public boolean deleteFile(String fileCode) {
		return fileService.delete(fileCode);
	}

	/**
	 * 上传文件
	 *
	 * @param file
	 * @return 文件Id
	 */
	@Override
	public String uploadFile(MultipartFile file) {

		String fileCode = UUID.randomUUID().toString().replace("-","");

		FileItem fileItem =  fileService.upload(file, fileCode);
		logger.debug("-------> Minio文件上传成功：{}",fileItem.toString());
		//保存文件映射关系
		FileInfo fileInfo = new FileInfo();
		fileInfo.setCode(fileCode);
		fileInfo.setName(fileItem.getName());
		fileInfo.setnName(fileItem.getCode());
		fileInfo.setSize(fileItem.getSize());
		fileInfo.setState(1);
		fileInfoMapper.addFileInfo(fileInfo);

		return fileCode;
	}
}
